/*
* Copyright (c) 2012 Kyle Hayward
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once

#include "SurfaceProgram.h"

namespace OptixDotNet 
{
	ref class Material;
	ref class Variable;

	/// <summary>
	/// A collection of <see cref="OptixDotNet::SurfaceProgram">SurfacePrograms</see>. Allows one to iterate through the SurfacePrograms contained by a Material.
    /// </summary>
	public ref class SurfaceProgramCollection
	{
	internal:
		SurfaceProgramCollection( Material^ container );
	public:

		/// <summary>
		/// Gets the number of ray types set on the parent Context
        /// </summary>
		property unsigned int Count
		{
			unsigned int get();
		}

		/// <summary>
		/// Gets or Sets the surface program associated with a (material, ray type) tuple.
        /// </summary>
		property SurfaceProgram^ default[int]
		{
			inline SurfaceProgram^ get ( int index );
			inline void set( int index, SurfaceProgram^ prog );
		}

	private:
		property Material^ Container;
		RayHitType mRayType;
	};

	/// <summary>
	/// Represents a set of <see cref="OptixDotNet::SurfaceProgram">SurfacePrograms</see> that will be executed when a ray intersects a primitive surface.
    /// </summary>
	public ref class Material : public OptixNode, IVariableContainer
	{
	public:

		/// <summary>
		/// Creates a new Material.
		/// </summary>
		Material( Context^ context );

		/// <summary>
		/// Checks to make sure the object is valid and consistent
        /// </summary>
		virtual void Validate() override
		{
			CheckError( rtMaterialValidate( mMtrl ) );
		}

		/// <summary>
		/// Destroys the internal Optix resource
        /// </summary>
		virtual void Destroy() override
		{
			if( mMtrl )
				CheckError( rtMaterialDestroy( mMtrl ) );

			mMtrl = 0;
		}

		/// <summary>
		/// Sets the surface program associated with a (material, ray type) tuple.
        /// </summary>
		/// <param name="rayTypeIndex">Index of the ray type</param>
		/// <returns>Surface program of the (material, ray type) tuple</returns>
		SurfaceProgram^ GetSurfaceProgram( unsigned int rayTypeIndex );

		/// <summary>
		/// Sets the surface program associated with a (material, ray type) tuple.
        /// </summary>
		/// <param name="rayTypeIndex">Index of the ray type</param>
		/// <param name="program">Surface program of the (material, ray type) tuple</param>
		void SetSurfaceProgram( unsigned int rayTypeIndex, SurfaceProgram^ program );

		/// <summary>
		/// Gets the collection of Surface Programs contained by the material
        /// </summary>
		property SurfaceProgramCollection^ Programs
		{
			inline SurfaceProgramCollection^ get() { return mCollection; }
		}

		/// <summary>
		/// Gets the number of global variables on the Material
        /// </summary>
		property int VariableCount
		{
			virtual int get()
			{
				unsigned int count;
				CheckError( rtMaterialGetVariableCount( mMtrl, &count ) );
				return count;
			}
		}

		/// <summary>
		/// Gets a local variable on the material with name [s]. If the variable doesn't exist, it is created.
        /// </summary>
		property Variable^ default[System::String^]
		{
			virtual Variable^ get( System::String^ s );
			virtual void set( System::String^s, Variable^ v );
		}

		/// <summary>
		/// Gets a local material variable at index [index]
        /// </summary>
		property Variable^ default[int]
		{
			virtual Variable^ get ( int index );
			virtual void set( int index, Variable^ v );
		}

	internal:
		Material( Context^ context, RTmaterial );
		inline RTmaterial InternalPtr() { return mMtrl; }

	private:
		RTmaterial	mMtrl;

		SurfaceProgramCollection^ mCollection;
	};
}